/* 
  Copyright (c) SilverLining 2015
  Date:    2016-1-7
  File:    shim_object.cpp  
  Description: 

*/

#include "stdafx.h"

using namespace System;
using namespace System::Security;
using namespace System::Runtime::InteropServices;

#include "shim_object.h"
#include "GameObject.h"
#include "Archiver.h"

using namespace m;
using namespace m::alloc;

namespace SL
{
	namespace M
	{
		shim_object::shim_object()
			: WriteCallback(nullptr),
			ReadCallback(nullptr),
			TypeName(nullptr)
		{
		}

		shim_object::~shim_object()
		{
			if (TypeName)
			{
				free(TypeName);
			}

			if (_hObject)
			{
				GCHandle hObject = GCHandle::FromIntPtr((IntPtr)_hObject);
				hObject.Free();
			}
		}

		void M::shim_object::set_object(GameObject ^ obj)
		{
			assert(obj != nullptr);
			if (obj != nullptr)
			{
				GCHandle hObject = GCHandle::Alloc(obj, GCHandleType::Weak);
				_hObject = ((IntPtr)hObject).ToPointer();

				IntPtr ipWGch = Marshal::GetFunctionPointerForDelegate(obj->_WriteDelegate);
				WriteCallback = static_cast<ObjectWriteCallback>(ipWGch.ToPointer());

				IntPtr ipRGch = Marshal::GetFunctionPointerForDelegate(obj->_ReadDelegate);
				ReadCallback = static_cast<ObjectReadCallback>(ipRGch.ToPointer());

				pin_ptr<const wchar_t> typeNameChars = PtrToStringChars(obj->GetType()->AssemblyQualifiedName);
				assign(TypeName, typeNameChars);
			}
		}

		void shim_object::read(archiver* archiver)
		{
			archiver->read_value(L"TypeName", &TypeName);
			
			if (!TypeName)
			{
				throw bad_result_exception(MONORET(FACILITY_OBJECT, ERROR_BAD_FORMAT), "Managed object missing a type.");
			}

			Type^ t = Type::GetType(gcnew String(TypeName));
			if (ReadCallback == nullptr)
			{
				GameObject^ obj = safe_cast<GameObject^>(Activator::CreateInstance(t));
				set_object(obj);
			}

			ReadCallback(archiver);
		}

		void shim_object::write(archiver* archiver)
		{
			archiver->write_value(L"TypeName", TypeName);
			WriteCallback(archiver);
		}

		GameObject ^ shim_object::get_object()
		{
			if (_hObject)
			{
				GCHandle hObject = GCHandle::FromIntPtr((IntPtr)_hObject);
				return safe_cast<GameObject^>(hObject.Target);
			}
			return nullptr;
		}

		std::wstring shim_object::to_string() const
		{
			return object::to_string() + L"+" + TypeName;
		}

		void * shim_object::operator new(size_t sz)
		{
			return ::operator new(sz, pool);
		}

		void shim_object::operator delete(void * ptr)
		{
			::operator delete(ptr, pool);
		}
	}
}